package com.example.serverdemo.netty.device_tcp_server.handler;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import com.alibaba.fastjson.JSON;
import com.example.serverdemo.bean.DeviceInfoBean;
import com.example.serverdemo.bean.json.device.LightControllerParameterBean;
import com.example.serverdemo.httputils.DeviceCommandHttpRequest;
import com.example.serverdemo.netty.device_tcp_server.api.DeviceAPI;
import com.example.serverdemo.netty.device_tcp_server.function_module.*;
import com.example.serverdemo.netty.device_tcp_server.type.FunctionType;
import com.example.serverdemo.service.DeviceService;
import com.example.serverdemo.service.HardwareParameterService;
import com.example.serverdemo.service.LightControllerService;
import com.example.serverdemo.service.ProtocolInfoService;
import com.example.serverdemo.serviceImpl.DeviceServiceImpl;
import com.example.serverdemo.serviceImpl.HardwareParameterServiceImpl;
import com.example.serverdemo.serviceImpl.LightControllerServiceImpl;
import com.example.serverdemo.serviceImpl.ProtocolInfoServiceImpl;
import com.example.serverdemo.utils.crcutls.CRCUtil;
import com.example.serverdemo.utils.log.PrintLogUtil;
import com.example.serverdemo.utils.redisutils.RedisUtils;
import com.example.serverdemo.utils.sessionutils.session.DeviceCommunicationSession;
import com.example.serverdemo.utils.sessionutils.sessionmanager.DeviceCommunicationSessionManager;
import com.example.serverdemo.utils.springutils.SpringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

import java.util.UUID;

import static com.example.serverdemo.common.Constant.*;

public class DeviceHandler extends BaseHandler implements DeviceAPI {
    public RedisUtils redisUtils;
    public DeviceCommandHttpRequest request;
    public DeviceService deviceService;
    public LightControllerService lightControllerService;
    public HardwareParameterService hardwareParameterService;
    public ProtocolInfoService protocolInfoService;
    private CoreControllerFunctionModule coreControllerFunctionModule;
    private LightingControllerFunctionModule lightingControllerFunctionModule;
    private CentralControllerFunctionModule centralControllerFunctionModule;
    private WallboardFunctionModule wallboardFunctionModule;
    private boolean loginStatus;
    public String deviceId;
    public DeviceInfoBean currentDeviceInfo;
    public FunctionType currentFunctionType;
    public String handlerSessionId;
    private boolean functionKey;


    public DeviceHandler(){
        this.redisUtils = SpringUtil.getBean(RedisUtils.class);
        this.request = SpringUtil.getBean(DeviceCommandHttpRequest.class);
        this.deviceService = SpringUtil.getBean(DeviceServiceImpl.class);
        this.protocolInfoService = SpringUtil.getBean(ProtocolInfoServiceImpl.class);
        this.lightControllerService = SpringUtil.getBean(LightControllerServiceImpl.class);
        this.hardwareParameterService = SpringUtil.getBean(HardwareParameterServiceImpl.class);
        this.handlerSessionId = UUID.randomUUID().toString();
        this.functionKey = true;
        this.loginStatus = false;
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] receiveMsgBytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(receiveMsgBytes);
        byteBuf.release();
        this.msgTransactionManagement(ctx,receiveMsgBytes);
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        this.logout();
        super.channelInactive(ctx);
    }
    public boolean closeHandler(){
        try {
            this.context.close();
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    public boolean isFunctionKey() {
        return functionKey;
    }
    public void setFunctionKey(boolean functionKey) {
        synchronized (this){
            this.functionKey = functionKey;
        }
    }
    private void msgTransactionManagement(ChannelHandlerContext ctx, byte[] msg){
        if (protocolHeaderDetection(msg)){
            if (this.loginStatus) withProtocolHeader(msg);
            else this.login(ctx,msg);
        }else {
            withoutProtocolHeader(ctx,msg);
        }
    }
    private boolean protocolHeaderDetection(byte[] msg){
        //CheckFlag
        if (msg[0] != FLAG1)return false;
        if (msg[1] != FLAG2)return false;
        if (msg[2] != SERVER_ADDRESS) return false;
        //CheckLength
        byte[] msgLength = Convert.intToBytes(msg.length - 8);
        if (msg[3] != msgLength[3]) return false;
        if (msg[4] != msgLength[2]) return false;
        //CheckCRC
        byte[] crcBUf = msg.clone();
        crcBUf[6] = DEFAULT_CRC_PLACEHOLDER;
        crcBUf[7] = DEFAULT_CRC_PLACEHOLDER;
        int crc16 = CRCUtil.getInstance().calcCrc16(crcBUf);
        byte[] crc = Convert.intToBytes(crc16);
        if (crc[3] != msg[6]) return false;
        if (crc[2] != msg[7]) return false;
        return true;
    }
    private void withProtocolHeader(byte[] msg){
        switch (currentFunctionType.getModuleType()){
            case CoreController:
                this.coreControllerFunctionModule.msgDistributionTransactionManagement(msg);
                break;
            case LightingController:
                this.lightingControllerFunctionModule.msgDistributionTransactionManagement(msg);
                break;
            case CentralController:
                this.centralControllerFunctionModule.msgDistributionTransactionManagement(msg);
                break;
            case Wallboard:
                this.wallboardFunctionModule.msgDistributionTransactionManagement(msg);
                break;
            case Null:
                break;
        }
    }
    private void withoutProtocolHeader(ChannelHandlerContext ctx,byte[] msg){
//        PrintLogUtil.printInfo(this,ctx.channel().remoteAddress() + "->" + StringUtils.printHexString(msg));
    }
    private boolean login(ChannelHandlerContext ctx,byte[] data){
        boolean flag = false;
        try {
            DeviceInfoBean deviceInfoBean = DeviceInfoBean.build(data);
            if (deviceInfoBean == null){
                return false;
            }else {
                if (this.checkIsRegister(deviceInfoBean)){
                    flag = this.updateLastConnectorTime(deviceInfoBean);
                }else {
                    flag = this.register(deviceInfoBean);
                }
            }
            if(flag){
                this.deviceId = deviceInfoBean.getDeviceId();
                this.currentDeviceInfo = deviceInfoBean;
                this.addHandlerSession(this.deviceId);
                this.addDeviceOnlineStatusOnRedis(this.deviceId);
                this.loginNotification(deviceInfoBean.getDeviceId());
                this.initFunctionModule(deviceInfoBean);
                this.loginStatus = true;
                this.readHardwareParameter();
            }
            PrintLogUtil.printInfo(this,this.handlerSessionId + "-Device " + deviceInfoBean.getDeviceName() + " : " + deviceId + " Login Success");
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return flag;
    }
    private boolean logout(){
        try {
            if (detectionSession(this.deviceId)){
                PrintLogUtil.printInfo(this,this.handlerSessionId + "-Device " + this.currentDeviceInfo.getDeviceName() + " : " + deviceId + " Logout Success");
                this.removeHandlerSession(this.deviceId);
                this.removeDeviceOnlineStatusOnRedis(this.deviceId);
                this.logoutNotification(this.deviceId);
            }
            this.closeFunctionModule();
            this.loginStatus = false;
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean checkIsRegister(DeviceInfoBean deviceInfoBean){
       try {
           return deviceService.queryDevice(deviceInfoBean.getDeviceId()) != null ;
       }catch (Exception e){
            PrintLogUtil.printError(this,e);
       }
       return false;
    }
    private boolean register(DeviceInfoBean deviceInfoBean){
        try {
            deviceInfoBean.setDeviceRegisterTime(DateTime.now());
            return this.deviceService.registerDevice(deviceInfoBean) > 0;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean updateLastConnectorTime(DeviceInfoBean deviceInfoBean){
        try {
            deviceInfoBean.setDeviceLastConnectedTime(DateTime.now());
            return this.deviceService.updateDevice(deviceInfoBean) > 0;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean detectionSession(String sessionId){
        try {
            DeviceHandler handler = (DeviceHandler) DeviceCommunicationSessionManager.getSession(sessionId).getAttribute(DEVICE_HANDLER);
            System.out.println("On Session : " + handler.handlerSessionId);
            System.out.println("On Logout Current : " + handlerSessionId);
            return handler.handlerSessionId.equals(this.handlerSessionId);
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean addHandlerSession(String sessionId){
        try {
            DeviceCommunicationSession session = null;
            if (DeviceCommunicationSessionManager.containsSession(sessionId)){
                session = DeviceCommunicationSessionManager.getSession(sessionId);
                synchronized (session) {
                    DeviceHandler handler = (DeviceHandler) DeviceCommunicationSessionManager.getSession(sessionId).getAttribute(DEVICE_HANDLER);
                    if (!handler.handlerSessionId.equals(this.handlerSessionId)) {
                        DeviceHandler oldHandler = (DeviceHandler) session.getAttribute(DEVICE_HANDLER);
                        session.addAttribute(DEVICE_HANDLER, this);
                        oldHandler.closeHandler();
                    }
                }
            }else {
                session = DeviceCommunicationSessionManager.addSession(sessionId);
                session.addAttribute(DEVICE_HANDLER,this);
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean removeHandlerSession(String sessionId){
        try {
            DeviceCommunicationSession session = DeviceCommunicationSessionManager.getSession(sessionId);
            if (session != null){
                synchronized (session){
                    DeviceHandler handler = (DeviceHandler) DeviceCommunicationSessionManager.getSession(sessionId).getAttribute(DEVICE_HANDLER);
                    if (handler.handlerSessionId.equals(this.handlerSessionId)){
                        DeviceCommunicationSessionManager.removeSession(session.getId());
                    }
                }
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean addDeviceOnlineStatusOnRedis(String deviceId){
        try {
            if (this.redisUtils.zSetQuery(DEVICE_ONLINE_STATUS_FLAG,deviceId)){
                this.redisUtils.zSetDelete(DEVICE_ONLINE_STATUS_FLAG,deviceId);
            }
            this.redisUtils.zSetAdd(DEVICE_ONLINE_STATUS_FLAG,deviceId,DateTime.now().getTime());
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean removeDeviceOnlineStatusOnRedis(String deviceId){
        try {
            if (this.redisUtils.zSetQuery(DEVICE_ONLINE_STATUS_FLAG,deviceId)){
                this.redisUtils.zSetDelete(DEVICE_ONLINE_STATUS_FLAG,deviceId);
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean loginNotification(String deviceId){
        try {
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean logoutNotification(String deviceId){
        try {
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean initFunctionModule(DeviceInfoBean deviceInfoBean){
        try {
            this.coreControllerFunctionModule = new CoreControllerFunctionModule(this);
            this.lightingControllerFunctionModule = new LightingControllerFunctionModule(this);
            this.centralControllerFunctionModule = new CentralControllerFunctionModule(this);
            this.wallboardFunctionModule = new WallboardFunctionModule(this);
            return  true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
       return false;
    }
    private boolean closeFunctionModule(){
        try {
            if (this.coreControllerFunctionModule != null){
                this.coreControllerFunctionModule.unBindDeviceHandler();
                this.coreControllerFunctionModule = null;
            }
            if (this.lightingControllerFunctionModule != null) {
                this.lightingControllerFunctionModule.unBindDeviceHandler();
                this.lightingControllerFunctionModule = null;
            }
            if (this.centralControllerFunctionModule != null){
                this.centralControllerFunctionModule.unBindDeviceHandler();
                this.centralControllerFunctionModule = null;
            }
            if (this.wallboardFunctionModule != null) {
                this.wallboardFunctionModule.unBindDeviceHandler();
                this.wallboardFunctionModule = null;
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    //API

    @Override
    public boolean restart() {
        try {
            if (this.coreControllerFunctionModule.restart()){
                PrintLogUtil.printInfo(this,"Restart Device Success");
                this.closeHandler();
                return true;
            }else {
                PrintLogUtil.printInfo(this,"Restart Device Failed");
            }
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }

    @Override
    public boolean remoteUpdate(String filePath) {
        try {
            this.currentFunctionType = FunctionType.FirmwareUpdate;
            return this.coreControllerFunctionModule.remoteUpdateFirmware(filePath);
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    public boolean readHardwareParameter() {
        try {
            this.currentFunctionType = FunctionType.HardwareParameterReadBack;
            return this.coreControllerFunctionModule.readBackHardwareParameter();
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    public boolean configurationHardwareParameter() {
        try {
            this.currentFunctionType = FunctionType.HardwareParameterConfiguration;
            return this.coreControllerFunctionModule.configurationHardwareParameter();
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    public boolean readBackLightControllerParameter() {
        try {
            this.currentFunctionType = FunctionType.LightingControllerParameterReadBack;
            return this.lightingControllerFunctionModule.readBackLightControllerParameter();
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    public boolean configurationLightControllerParameter(String parameterJsonStr) {
        try {
            this.currentFunctionType = FunctionType.LightingControllerParameterReadBack;
            LightControllerParameterBean bean = JSON.parseObject(parameterJsonStr, LightControllerParameterBean.class);
            return this.lightingControllerFunctionModule.configurationLightControllerParameter(bean);
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    public boolean configurationCentralControllerProtocol(int protocolId) {
        try {
            this.currentFunctionType = FunctionType.CentralControllerProtocolConfiguration;
            return this.centralControllerFunctionModule.configurationProtocol(protocolId);
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
}
